<template>
    <div class="gems-tooltip" ref="popperContainerNode"  v-on="outerEvents" >
        <div class="gems-tooltip__trigger" ref="triggerNode" v-on="events">
            <slot />
        </div>
        <Transition :name="transition">
            <div v-if="showPopper" class="gems-tooltip__popper" ref="popperNode" v-on="innerEvents">
                <slot name="content" >{{ content }}</slot>
                <div id="arrow" data-popper-arrow></div>
            </div>
        </Transition>
    </div>
</template>

<script lang="ts" setup>
import { onUnmounted, ref, watch, computed, reactive } from 'vue'
import useClickOutside from '../../hooks/useClickOutside'
import type { TooltipProps, TooltipEmits, TooltipInstance } from './types'
import { createPopper } from '@popperjs/core'
import type { Instance } from '@popperjs/core'
import { debounce } from 'lodash-es'


defineOptions({
    name: 'GemsTooltip',
})

const props = withDefaults(defineProps<TooltipProps>(), {
    placement: 'bottom',
    trigger: 'hover',
    openDelay: 0,
    closeDelay: 0,
    transition: 'fade',
})
const emits = defineEmits<TooltipEmits>()

const showPopper = ref(false)

const triggerNode = ref<HTMLElement>()
const popperNode = ref<HTMLElement>()
const popperContainerNode = ref<HTMLElement>()

let popperInstance: null | Instance = null
// 动态事件---根据属性不同进行事件的切换 --- 一定要用reactive!!!
let events: Record<string, any> = reactive({})
// 给他俩父亲也绑定一个事件--解决鼠标移入popperNode时（尽管移出triggerNode了，但在popperNode上），popperNode不显示的bug
let outerEvents: Record<string, any> = reactive({})
let innerEvents: Record<string, any> = reactive({})
// 添加一个默认时间，添加一个 50 毫秒的关闭 delay，解决当 鼠标从triggerNode划入到popperNode时，经过中间的小间隙倒置立即触发close事件的bug
const closeDelay = computed(() => 
  props.trigger === 'hover' && props.closeDelay === 0 ? 50 : props.closeDelay
)


// 支持proper的Options
const popperOptions = computed(() => {
    return {
        placement: props.placement,
        // 箭头的偏移
        modifiers: [
            {
                name: 'offset',
                options: {
                    offset: [0, 9],
                },
            },
        ],
        ...props.popperOptions,
    }
})

// hover事件
const open = () => {
    showPopper.value = true
    emits('visible-change', true)
}
const close = () => {
    showPopper.value = false
    emits('visible-change', false)
}

// 延迟事件--防抖触发
const openDebounce = debounce(open, props.openDelay)
const closeDebounce = debounce(close, closeDelay.value)

const openFinal = () => {
    // 先清除之前的关闭延迟事件-- 不然会执行关闭的，浪费资源
    // console.log('openFinal 执行了');
    closeDebounce.cancel()
    openDebounce()
}
const closeFinal = () => {
    // 先清除之前的打开延迟事件
    openDebounce.cancel()
    closeDebounce()
}

// 点击事件
const togglePopper = () => {
    if (showPopper.value) {
        closeFinal()
    } else {
        openFinal()
    }
}
// 点击组件外侧，隐藏popperNode
useClickOutside(popperContainerNode, () => {
    if (props.trigger === 'click' && showPopper.value && !props.manual)
        closeFinal()
    if (showPopper.value) {
        emits('click-outside', true)
    }
})

// 动态事件切换绑定
const attachEvents = () => {
    if (props.trigger === 'click') {
        events['click'] = togglePopper
    } else if (props.trigger === 'hover') {
        events['mouseenter'] = openFinal
        outerEvents['mouseleave'] = closeFinal
        // 中间有间隙，会造成dropdown在hover时关闭的bug
        innerEvents['mouseenter'] = openFinal
        // console.log('events', events);
        // console.log('outerEvents', outerEvents);
        // console.log('showPopper', showPopper.value);
    }
}
// 手动模式下，不绑定事件
if (!props.manual) {
    attachEvents()
}

// 监听showPopper变化，动态创建或销毁popper实例
watch(showPopper, (value) => {
    if (value) {
        if (triggerNode.value && popperNode.value) {
            popperInstance = createPopper(triggerNode.value, popperNode.value, popperOptions.value)
        } else {
            popperInstance?.destroy()
        }
    }
}, { flush: 'post' })

onUnmounted(() => {
    popperInstance?.destroy()
})

// 监听props.trigger变化，重新绑定事件
watch(() => props.trigger, (newTrigger, oldTrigger) => {
    if (newTrigger !== oldTrigger) {
        // 清空原事件
        events = {}
        outerEvents = {}
        innerEvents = {}
        // 重新绑定事件
        attachEvents()
    }
})

// 
watch(() => props.manual, (isManual) => {
    if (isManual) {
        // 手动模式下，不绑定事件
        events = {}
        outerEvents = {}
        innerEvents = {}
    } else {
        // 动态模式下，重新绑定事件
        attachEvents()
    }
})

defineExpose<TooltipInstance>({
    'show': openFinal,
    'hide': closeFinal,
})
</script>